package com.qf.locked.aspect;

import com.qf.locked.aspect.annotation.DistributedLock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 定义切面
 * 定义切点（切入点表达式、注解）
 * 定义通知
 */
//分布式锁切面
@Aspect
@Component
public class DistributeLockedAscept {
    //redis锁名的前缀
    public static final String LOCKED_PREFIX_KEY = "distributed:locked:";
    //默认锁名（redis的key）
    public static final String DEFAULT_LOCK_NAME = "default";
    //加锁默认等待时间
    public static final long DEFAULT_WAIT_TIME = 3;
    //看门狗（守护线程）最长延长时间
    public static final long DEFAULT_LEASE_TIME = 10;

    @Resource
    RedissonClient redissonClient;

    //定义切点
    @Pointcut("@annotation(com.qf.locked.aspect.annotation.DistributedLock)")
    public void pointCut(){

    }

    //定义通知(环绕通知)
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint pjp){
        //方法的返回值
        Object obj = null;
        //操作代理或被代理的对象
        MethodSignature signature = (MethodSignature)pjp.getSignature();
        //获取被代理的方法
        Method method = signature.getMethod();
        //通过方法获取方法上的注解
        DistributedLock distributedLock = method.getAnnotation(DistributedLock.class);
        //通过注解的lockType属性值获取锁的方法
        RLock rLock = getLock(distributedLock);
        //获取加锁的超时时间
        long waitTime = distributedLock.waitTime()>0?distributedLock.waitTime():DEFAULT_WAIT_TIME;
        //获取看门狗守护时间
        long leastTime = distributedLock.LeaseTime()>0? distributedLock.LeaseTime() : DEFAULT_LEASE_TIME;
        //获取时间默认单位
        TimeUnit unitTime = distributedLock.unit();

            try {
                //加锁
                boolean b = rLock.tryLock();
                if (b){
                    //执行方法并获取返回值
                    obj = pjp.proceed();
                }
            } catch (Throwable e) {
                //待完善（抛出异常 全局异常处理）
                e.printStackTrace();
            }finally {
                //解锁
                rLock.unlock();
            }

        return obj;
    }


    public RLock getLock(DistributedLock distributedLock){
        String lockName = !"".equals(distributedLock.lockName())? distributedLock.lockName() : DEFAULT_LOCK_NAME;
        LockTypeEnum lockTypeEnum = distributedLock.lockType();

        RLock rLock = null;
        RLock[] rlocks=null;

        String[] lockNames=null;
        switch (lockTypeEnum){
            //联锁
            case MULTI:
                lockNames = distributedLock.lockNames();
                rlocks = new RLock[lockNames.length];

                if (lockNames.length>0){
                    for (int i = 0; i < lockNames.length; i++) {
                        RLock lock = redissonClient.getLock(LOCKED_PREFIX_KEY+lockNames[i]);
                        rlocks[i]=lock;
                    }
                    rLock = new RedissonMultiLock(rlocks);
                }else {
                    RLock lock = redissonClient.getLock(LOCKED_PREFIX_KEY+lockName);
                    rLock = new RedissonMultiLock(lock);
                }
                break;
                //红锁
            case RED:
                lockNames = distributedLock.lockNames();
                rlocks = new RLock[lockNames.length];

                if (lockNames.length>0){
                    for (int i = 0; i < lockNames.length; i++) {
                        RLock lock = redissonClient.getLock(LOCKED_PREFIX_KEY+lockNames[i]);
                        rlocks[i]=lock;
                    }
                    rLock = new RedissonRedLock(rlocks);
                }else {
                    RLock lock = redissonClient.getLock(LOCKED_PREFIX_KEY+lockName);
                    rLock = new RedissonRedLock(lock);
                }
                break;
                //公平锁
            case FAIR:
                rLock = redissonClient.getFairLock(LOCKED_PREFIX_KEY+lockName);
                break;
                //读写锁
            case READ_WRITE:
                //该对象有两个方法readLock（）和writeLock（）分别为获取读锁或者写锁
                RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(LOCKED_PREFIX_KEY + lockName);
                rLock = distributedLock.ReadWrite().equals(ReadWriteEnum.READ)? readWriteLock.readLock() : readWriteLock.writeLock();
                break;
                //默认是REENTRANT（可重入锁）
            default:
                rLock = redissonClient.getLock(LOCKED_PREFIX_KEY + lockName);
                break;


        }
        return rLock;
    }


}
